home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / language / harvest.cpt / Harvest C / Tcl 6.2 / tclGlob.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-14  |  21.2 KB  |  862 lines

  1.  
  2. #ifdef macintosh
  3. #    pragma segment TCLGLOB
  4. #endif
  5.  
  6. /* 
  7.  * tclGlob.c --
  8.  *
  9.  *    This file provides procedures and commands for file name
  10.  *    manipulation, such as tilde expansion and globbing.
  11.  *
  12.  * Copyright 1990 Regents of the University of California
  13.  * Permission to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose and without
  15.  * fee is hereby granted, provided that the above copyright
  16.  * notice appear in all copies.  The University of California
  17.  * makes no representations about the suitability of this
  18.  * software for any purpose.  It is provided "as is" without
  19.  * express or implied warranty.
  20.  */
  21.  
  22. /* 
  23.  * Copyright 1990 Karl Lehenbauer, Mark Diekhans, 
  24.  *                Peter da Silva and Jordan Henderson.
  25.  *
  26.  * Permission to use, copy, modify, and distribute this
  27.  * software and its documentation for any purpose and without
  28.  * fee is hereby granted, provided that the above copyright
  29.  * notice appear in all copies.  Karl Lehenbauer, Mark Diekhans,
  30.  * Peter da Silva and Jordan Henderson make no representations
  31.  * about the suitability of this software for any purpose.
  32.  * It is provided "as is" without express or implied warranty.
  33.  */
  34.  
  35. /* 
  36.  * Copyright 1992 Tim Endres
  37.  *
  38.  * Permission to use, copy, modify, and distribute this
  39.  * software and its documentation for any purpose and without
  40.  * fee is hereby granted, provided that the above copyright
  41.  * notice appear in all copies. Tim Endres makes no representations
  42.  * about the suitability of this software for any purpose.
  43.  * It is provided "as is" without express or implied warranty.
  44.  */
  45.  
  46. #ifndef NOSCCSID
  47.     static char *sccs_id = "%M% %I% %H%";
  48. #endif
  49.  
  50. #ifdef THINK_C
  51. #define macintosh
  52. #endif
  53.  
  54. #include <stdio.h>
  55. #include <errno.h>
  56.  
  57. #ifndef macintosh
  58. #    include <pwd.h>
  59. #endif
  60.  
  61. #if defined(SYSV_3_2_0) || defined(XENIX286)
  62. #  include "../../ossupport/src/stdlib.h"
  63. #  include "../../ossupport/src/string.h"
  64. #else
  65. #  ifndef BSD
  66. #    include <stdlib.h>
  67. #  endif
  68. #  include <string.h>
  69. #endif
  70.  
  71. #ifdef macintosh
  72. #    include <types.h>
  73. #else
  74. #    include <sys/types.h>
  75. #endif
  76.  
  77. #if defined(BSD) || defined(HPUX)
  78. #  include <sys/dir.h>
  79. #endif
  80.  
  81. #include "tcl.h"
  82.  
  83. #ifdef macintosh
  84. #ifndef THINK_C
  85. #    include <files.h>
  86. #    include <errors.h>
  87. #endif
  88. #    include "stat.h"
  89. #    define FALSE    0
  90. #else
  91. #    include <sys/stat.h>
  92. #endif
  93.  
  94. #if defined(SYSV_3_2_0) || defined(XENIX286)
  95. #  include "../../ossupport/src/ndir.h"
  96. #endif
  97. #if defined(SYSV_3_2_2) || defined(XENIX386)
  98. #  include <dirent.h>
  99. #endif
  100.  
  101. #ifdef XENIX
  102.    extern int errno;
  103.    extern struct passwd *getpwnam();
  104. #endif
  105.  
  106. extern char *getenv();
  107. #include "tclInt.h"
  108.  
  109. #ifdef THINK_C
  110. #include "CTclShell.h"
  111. extern CTclShell *gShell;
  112. #define Feedback gShell->Hprintf
  113. #endif
  114.  
  115. static int        _glob_debug_level = 0;
  116. static int        _glob_show_invisibles = 0;
  117.  
  118. static int        _glob_show_type = 0;
  119. static int        _glob_show_creator = 0;
  120. static ResType    _glob_type;
  121. static ResType    _glob_creator;
  122.  
  123.  
  124. /*
  125.  * The structure below is used to keep track of a globbing result
  126.  * being built up (i.e. a partial list of file names).  The list
  127.  * grows dynamically to be as big as needed.
  128.  */
  129.  
  130. typedef struct {
  131.     char *result;        /* Pointer to result area. */
  132.     int totalSpace;        /* Total number of characters allocated
  133.                  * for result. */
  134.     int spaceUsed;        /* Number of characters currently in use
  135.                  * to hold the partial result (not including
  136.                  * the terminating NULL). */
  137.     int dynamic;        /* 0 means result is static space, 1 means
  138.                  * it's dynamic. */
  139. } GlobResult;
  140.  
  141. /*
  142.  *----------------------------------------------------------------------
  143.  *
  144.  * AppendResult --
  145.  *
  146.  *    Given two parts of a file name (directory and element within
  147.  *    directory), concatenate the two together and add them to a
  148.  *    partially-formed result.
  149.  *
  150.  * Results:
  151.  *    There is no return value.  The structure at *resPtr is modified
  152.  *    to hold more information.
  153.  *
  154.  * Side effects:
  155.  *    Storage may be allocated if we run out of space in *resPtr.
  156.  *
  157.  *----------------------------------------------------------------------
  158.  */
  159.  
  160. /*
  161.  *----------------------------------------------------------------------
  162.  *
  163.  * AppendResult --
  164.  *
  165.  *    Given two parts of a file name (directory and element within
  166.  *    directory), concatenate the two together and append them to
  167.  *    the result building up in interp.
  168.  *
  169.  * Results:
  170.  *    There is no return value.
  171.  *
  172.  * Side effects:
  173.  *    Interp->result gets extended.
  174.  *
  175.  *----------------------------------------------------------------------
  176.  */
  177.  
  178. static void
  179. AppendResult(interp, dir, name, nameLength)
  180.     Tcl_Interp *interp;        /* Interpreter whose result should be
  181.                  * appended to. */
  182.     char *dir;            /* Name of directory, with trailing
  183.                  * slash (unless the whole string is
  184.                  * empty). */
  185.     char *name;            /* Name of file withing directory (NOT
  186.                  * necessarily null-terminated!). */
  187.     int nameLength;        /* Number of characters in name. */
  188. {
  189.     int dirLength, dirFlags, nameFlags;
  190.     char *p, saved;
  191.  
  192.     /*
  193.      * Next, see if we can put together a valid list element from dir
  194.      * and name by calling Tcl_AppendResult.
  195.      */
  196.  
  197.     if (*dir == 0) {
  198.         dirFlags = 0;
  199.         }
  200.     else {
  201.         Tcl_ScanElement(dir, &dirFlags);
  202.         }
  203.     saved = name[nameLength];
  204.     name[nameLength] = 0;
  205.     Tcl_ScanElement(name, &nameFlags);
  206.     if ((dirFlags == 0) && (nameFlags == 0)) {
  207.         /*if (_glob_debug_level>5)
  208.             Feedback("GLOB: APPEND RESULT name '%.*s' result x%lx '%s' ",
  209.                         dir, nameLength, name, interp->result, interp->result);*/
  210.         if (interp->result != NULL && *(interp->result) != '\0') {
  211.             if (_glob_debug_level>5)
  212.                 Feedback("GLOB: APPEND RESULT ADD SPACE ");
  213.             Tcl_AppendResult(interp, " ", dir, name, (char *) NULL);
  214.             }
  215.         else {
  216.             Tcl_AppendResult(interp, dir, name, (char *) NULL);
  217.             }
  218.         name[nameLength] = saved;
  219.         return;
  220.         }
  221.  
  222.     /*
  223.      * This name has weird characters in it, so we have to convert it to
  224.      * a list element.  To do that, we have to merge the characters
  225.      * into a single name.  To do that, malloc a buffer to hold everything.
  226.      */
  227.  
  228.     dirLength = strlen(dir);
  229.     p = (char *) ckalloc((unsigned) (dirLength + nameLength + 1));
  230.     strcpy(p, dir);
  231.     strcpy(p+dirLength, name);
  232.     name[nameLength] = saved;
  233.     Tcl_AppendElement(interp, p, 0);
  234.     ckfree(p);
  235. }
  236.  
  237.  
  238. /*
  239.  *----------------------------------------------------------------------
  240.  *
  241.  * DoGlob --
  242.  *
  243.  *    This recursive procedure forms the heart of the globbing
  244.  *    code.  It performs a depth-first traversal of the tree
  245.  *    given by the path name to be globbed.
  246.  *
  247.  * Results:
  248.  *    The return value is a standard Tcl result indicating whether
  249.  *    an error occurred in globbing.  The result in interp will be
  250.  *    set to hold an error message, if any.  The result pointed
  251.  *    to by resPtr is updated to hold all file names given by
  252.  *    the dir and rem arguments.
  253.  *
  254.  * Side effects:
  255.  *    None.
  256.  *
  257.  *----------------------------------------------------------------------
  258.  */
  259.  
  260.  
  261. spacencount(str, len)
  262. char    *str;
  263. int        len;
  264. {
  265. int        count;
  266.  
  267.     if (len == -1)
  268.         len = strlen(str);
  269.     
  270.     for (count=0 ; *str && len-- > 0 ; str++)
  271.         if (*str == ' ')
  272.             count++;
  273.     
  274.     return count;
  275.     }
  276.  
  277. escape_spaces(name)
  278. char    *name;
  279. {
  280. char    buffer[512], *ptr1, *ptr2;
  281.  
  282.     for (ptr1=name,ptr2=buffer ; *ptr1 ; ) {
  283.         if (*ptr1 == ' ' && ptr1 > name && *(ptr1-1) != '\\')
  284.             *ptr2++ = '\\';
  285.         *ptr2++ = *ptr1++;
  286.         }
  287.     *ptr2 = '\0';
  288.     for (ptr1=name, ptr2=buffer ; *ptr1++ = *ptr2++ ; )
  289.         ;
  290.     }
  291.  
  292.  
  293. static int
  294. DoGlob(interp, dir, rem/*, resPtr*/)
  295. Tcl_Interp *interp;        /* Interpreter to use for error
  296.                         ** reporting (e.g. unmatched brace).
  297.                         */
  298. char *dir;                /* Name of a directory at which to
  299.                         ** start glob expansion.  This name
  300.                         ** is fixed: it doesn't contain any
  301.                         ** globbing chars.  If it's non-empty
  302.                         ** then it should end with a colon.
  303.                         */
  304. char *rem;                /* Path to glob-expand. */
  305. /*GlobResult *resPtr;    */    /* Where to store fully-expanded file names.*/
  306. {
  307. char    c,
  308.         *p,
  309.         *openBrace,
  310.         *closeBrace;
  311. char    mac_name[256];
  312. int        gotSpecial,
  313.         result;
  314.  
  315.     /*
  316.     ** When this procedure is entered, the name to be globbed may
  317.     ** already have been partly expanded by ancestor invocations of
  318.     ** DoGlob.  The part that's already been expanded is in "dir"
  319.     ** (this may initially be empty), and the part still to expand
  320.     ** is in "rem".  This procedure expands "rem" one level, making
  321.     ** recursive calls to itself if there's still more stuff left
  322.     ** in the remainder.
  323.     */
  324.  
  325.     /*
  326.     ** When generating information for the next lower call,
  327.     ** use static areas if the name is short, and malloc if the name
  328.     ** is longer.
  329.     */
  330.  
  331. #ifdef macintosh
  332.     RotateCursor(32);
  333. #endif
  334.  
  335.     if (_glob_debug_level)
  336.         Feedback("GLOB: dir <%s> rem <%s> ", dir, rem);
  337.     
  338. #define STATIC_SIZE 200
  339.  
  340.     /*
  341.     ** First, find the end of the next element in rem, checking
  342.     ** along the way for special globbing characters.
  343.     */
  344.  
  345.     gotSpecial = 0;
  346.     openBrace = closeBrace = NULL;
  347.     for (p = rem ; ; p++) {
  348.         c = *p;
  349.         if ((c == '\0') || (c == ':')) {
  350.             break;
  351.             }
  352.         if ((c == '{') && (openBrace == NULL)) {
  353.             openBrace = p;
  354.             }
  355.         if ((c == '}') && (closeBrace == NULL)) {
  356.             closeBrace = p;
  357.             }
  358.         if ((c == '*') || (c == '[') || (c == '\\') || (c == '?')) {
  359.             gotSpecial = 1;
  360.             }
  361.         }
  362.  
  363.     /*
  364.     ** If there is an open brace in the argument, then make a recursive
  365.     ** call for each element between the braces.  In this case, the
  366.     ** recursive call to DoGlob uses the same "dir" that we got.
  367.     ** If there are several brace-pairs in a single name, we just handle
  368.     ** one here, and the others will be handled in recursive calls.
  369.     */
  370.     if (openBrace != NULL) {
  371.         int remLength, l1, l2;
  372.         char static1[STATIC_SIZE];
  373.         char *element, *newRem;
  374.     
  375.         if (closeBrace == NULL)
  376.             {
  377.             Tcl_ResetResult(interp);
  378.             interp->result = "unmatched open-brace in file name";
  379.             return TCL_ERROR;
  380.             }
  381.     
  382.         remLength = strlen(rem) + 1;
  383.         if (remLength <= STATIC_SIZE) {
  384.             newRem = static1;
  385.             }
  386.         else {
  387.             newRem = ckalloc((unsigned) remLength);
  388.             }
  389.         l1 = openBrace-rem;
  390.         strncpy(newRem, rem, l1);
  391.         p = openBrace;
  392.         for (p = openBrace; *p != '}'; )
  393.             {
  394.             element = p+1;
  395.             for (p = element; ((*p != '}') && (*p != ',')); p++)
  396.                 {
  397.                 /* Empty body:  just find end of this element. */
  398.                 }
  399.             l2 = p - element;
  400.             strncpy(newRem+l1, element, l2);
  401.             strcpy(newRem+l1+l2, closeBrace+1);
  402.             if (DoGlob(interp, dir, newRem/*, resPtr*/) != TCL_OK)
  403.                 {
  404.                 return TCL_ERROR;
  405.                 }
  406.             }
  407.         
  408.         if (remLength > STATIC_SIZE)
  409.             {
  410.             ckfree((char *)newRem);
  411.             }
  412.         
  413.         return TCL_OK;
  414.         }
  415.  
  416.     /*
  417.     ** If there were any pattern-matching characters, then scan through
  418.     ** the directory to find all the matching names.
  419.     */
  420.     if (gotSpecial) {
  421.         char        *mac_name_ptr = dir;    /* Use for > 256 chars */
  422.         int            l1, l2;
  423.         int            i, index, vrefnum;
  424.         long        dirid;
  425.         char        *pattern, *newDir;
  426.         char        static1[STATIC_SIZE], static2[STATIC_SIZE];
  427.         char        volname[32];
  428.         CInfoPBRec        cpb;
  429.         ParamBlockRec    pb;
  430.     
  431.         if (*dir)
  432.             strcpy(mac_name, dir);
  433.         else
  434.             strcpy(mac_name, ":");
  435.         
  436.         vrefnum = 0;
  437.         dirid = 0;
  438.  
  439.         if (mac_name[0] == ':') {
  440.             /* RELATIVE */
  441.             WDPBRec    wpb;
  442.             
  443.             wpb.ioCompletion = 0;
  444.             wpb.ioNamePtr = NULL;
  445.             PBHGetVol(&wpb, FALSE);
  446.             
  447.             vrefnum = wpb.ioWDVRefNum;
  448.             dirid = wpb.ioWDDirID;
  449.             }
  450.         else {
  451.             /* ABSOLUTE */
  452.             for (i = 0 ; mac_name[i] != '\0' ; i++) {
  453.                 if (mac_name[i] == ':')
  454.                     break;
  455.                 }
  456.             if (mac_name[i] == ':' && i < 27) {
  457.                 strncpy(&volname[1], mac_name, i + 1);
  458.                 volname[0] = i + 1;
  459.                 pb.volumeParam.ioCompletion = 0;
  460.                 pb.volumeParam.ioNamePtr = (unsigned char *) volname;
  461.                 pb.volumeParam.ioVRefNum = 0;
  462.                 pb.volumeParam.ioVolIndex = -1;
  463.                 PBGetVInfo(&pb, FALSE);
  464.                 if (_glob_debug_level)
  465.                     Feedback("GLOB: SCAN: ABSOLUTE=%d Vol <%.*s>=%d ",
  466.                                 pb.volumeParam.ioResult,
  467.                                 volname[0], &volname[1], pb.volumeParam.ioVRefNum);
  468.                 if (pb.volumeParam.ioResult == noErr) {
  469.                     vrefnum = pb.volumeParam.ioVRefNum;
  470.                     dirid = fsRtParID;
  471.                     }
  472.                 }
  473.             }
  474.         
  475.         c2pstr(mac_name);
  476.         
  477.         cpb.hFileInfo.ioCompletion = 0;
  478.         cpb.hFileInfo.ioVRefNum = vrefnum;
  479.         cpb.hFileInfo.ioNamePtr = (unsigned char *) mac_name;
  480.         cpb.hFileInfo.ioFDirIndex = 0;            /* Use name... */
  481.         cpb.hFileInfo.ioDirID = dirid;
  482.         result = PBGetCatInfo(&cpb, FALSE);
  483.         if (_glob_debug_level/* > 1 || (_glob_debug_level && result)*/)
  484.             Feedback("GLOB: SCAN: RESULT %d getting DIR <%.*s> ",
  485.                         result, mac_name[0], &mac_name[1]);
  486.         if (result != noErr) {
  487.             if (result == fnfErr)
  488.                 return TCL_OK;
  489.             else
  490.                 {
  491.                 Tcl_ResetResult(interp);
  492.                 Tcl_AppendResult(interp, "couldn't read directory \"",
  493.                                     ( (*dir) ? dir : ":" ), "\" ", (char *) NULL);
  494.                 return TCL_ERROR;
  495.                 }
  496.             }
  497.         else if ((cpb.hFileInfo.ioFlAttrib & ioDirMask) == 0) {
  498.             Tcl_ResetResult(interp);
  499.             Tcl_AppendResult(interp, "directory \"",
  500.                                 ( (*dir) ? dir : ":" ), "\" is a file", (char *) NULL);
  501.             return TCL_ERROR;
  502.             }
  503.  
  504.         dirid = cpb.hFileInfo.ioDirID;
  505.         if (_glob_debug_level)
  506.             Feedback("GLOB: gotSpecial <%s> vRef %d dirID %ld ",
  507.                         (*dir ? dir : ":" ), cpb.hFileInfo.ioVRefNum, dirid);
  508.  
  509.         l1 = strlen(dir);
  510.         l2 = (p - rem);
  511.         if (l2 < STATIC_SIZE) {
  512.             pattern = static2;
  513.             }
  514.         else {
  515.             pattern = ckalloc((unsigned) (l2+1));
  516.             }
  517.         strncpy(pattern, rem, l2);
  518.         pattern[l2] = '\0';
  519.         
  520.         result = TCL_OK;
  521.         for (index=1 ; ; index++) {
  522.             
  523. #ifdef NEVER_DEFINED
  524.             RotateCursor(32);
  525. #endif
  526.             
  527.             cpb.hFileInfo.ioCompletion = 0;
  528.             cpb.hFileInfo.ioVRefNum = vrefnum;
  529.             cpb.hFileInfo.ioNamePtr = (unsigned char *) mac_name; mac_name[0] = '\0';
  530.             cpb.hFileInfo.ioFDirIndex = index;
  531.             cpb.hFileInfo.ioDirID = dirid;
  532.             result = PBGetCatInfo(&cpb, FALSE);
  533.             if ( _glob_debug_level > 1 || (_glob_debug_level && result) )
  534.                 Feedback("GLOB: INDEX-%03d = %d, <%.*s> [%ld]",
  535.                             index, result, mac_name[0], &mac_name[1], dirid);
  536.  
  537.             if (result == fnfErr) {
  538.                 result = TCL_OK;
  539.                 break;
  540.                 }
  541.  
  542.             if (! _glob_show_invisibles)
  543.                 if ((cpb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible) != 0) {
  544.                     if (_glob_debug_level)
  545.                         Feedback("GLOB: SKIP INVISIBLE <%.*s> ", mac_name[0], &mac_name[1]);
  546.                     continue;
  547.                     }
  548.             
  549.             if (_glob_show_type)
  550.                 if (cpb.hFileInfo.ioFlFndrInfo.fdType != _glob_type) {
  551.                     if (_glob_debug_level)
  552.                         Feedback("GLOB: SKIP !TYPE '%4.4s' <%.*s> ",
  553.                                     &cpb.hFileInfo.ioFlFndrInfo.fdType, mac_name[0], &mac_name[1]);
  554.                     continue;
  555.                     }
  556.             
  557.             if (_glob_show_creator)
  558.                 if (cpb.hFileInfo.ioFlFndrInfo.fdCreator != _glob_creator) {
  559.                     if (_glob_debug_level)
  560.                         Feedback("GLOB: SKIP !CREATOR '%4.4s' <%.*s> ",
  561.                                     &cpb.hFileInfo.ioFlFndrInfo.fdCreator, mac_name[0], &mac_name[1]);
  562.                     continue;
  563.                     }
  564.             
  565.             p2cstr(mac_name);
  566.             
  567.             /*
  568.              * Don't match names starting with "." unless the "." is
  569.              * present in the pattern.
  570.              */
  571.             if ((*mac_name == '.') && (*pattern != '.')) {
  572.                 if (_glob_debug_level)
  573.                     Feedback("GLOB: SKIP DOT FILE <%.*s> ", mac_name[0], &mac_name[1]);
  574.                 continue;
  575.                 }
  576.             if ((*mac_name == 'Ñ') && (*pattern != 'Ñ')) {
  577.                 if (_glob_debug_level)
  578.                     Feedback("GLOB: SKIP SPOT FILE <%.*s> ", mac_name[0], &mac_name[1]);
  579.                 continue;
  580.                 }
  581.  
  582.             if (Tcl_StringMatch(mac_name, pattern)) {
  583.                 int nameLen;
  584.                 
  585.                 nameLen = strlen (mac_name);
  586.                 
  587.                 if (*p == '\0') {
  588.                     AppendResult(interp, dir, mac_name, nameLen/*, resPtr*/);
  589.                     }
  590.                 else {
  591.                     if ((l1 + nameLen + 2) <= STATIC_SIZE) {
  592.                         newDir = static1;
  593.                         }
  594.                     else {
  595.                         newDir = ckalloc((unsigned) (l1 + nameLen + 2));
  596.                         }
  597.                     sprintf(newDir, "%s%s:", dir, mac_name);
  598.                     result = DoGlob(interp, newDir, p+1/*, resPtr*/);
  599.                     if (newDir != static1) {
  600.                         ckfree((char *) newDir);
  601.                         }
  602.                     
  603.                     if (result != TCL_OK)
  604.                         break;
  605.                     }
  606.                 }
  607.             else {
  608.                 if (_glob_debug_level)
  609.                     Feedback("GLOB: SKIP !MATCH <%.*s> <%s> ",
  610.                                 mac_name[0], &mac_name[1], pattern);
  611.                 }
  612.             }
  613.         
  614.         if (pattern != static2)
  615.             {
  616.             ckfree((char *)pattern);
  617.             }
  618.  
  619.         return result;
  620.         }
  621.  
  622.     /*
  623.     ** This is the simplest case:  just another path element.  Move
  624.     ** it to the dir side and recurse (or just add the name to the
  625.     ** list, if we're at the end of the path).
  626.     */
  627.     if (*p == 0) {
  628.         AppendResult(interp, dir, rem, (int)(p-rem)/*, resPtr*/);
  629.         }
  630.     else {
  631.         int l1, l2;
  632.         char *newDir;
  633.         char static1[STATIC_SIZE];
  634.     
  635.         l1 = strlen(dir);
  636.         l2 = l1 + (int)(p - rem) + 2;
  637.         if (l2 <= STATIC_SIZE)
  638.             {
  639.             newDir = static1;
  640.             }
  641.         else {
  642.             newDir = ckalloc((unsigned) l2);
  643.             }
  644.         strcpy(newDir, dir);
  645.         strncpy(newDir+l1, rem, (int)(p - rem));
  646.         newDir[l2-2] = ':';
  647.         newDir[l2-1] = 0;
  648.         result = DoGlob(interp, newDir, p+1/*, resPtr*/);
  649.         if (newDir != static1) {
  650.             ckfree((char *) newDir);
  651.             }
  652.         if (result != TCL_OK) {
  653.             return TCL_ERROR;
  654.             }
  655.         }
  656.     
  657.     return TCL_OK;
  658.     }
  659.  
  660. /*
  661.  *----------------------------------------------------------------------
  662.  *
  663.  * Tcl_TildeSubst --
  664.  *
  665.  *    Given a name starting with a tilde, produce a name where
  666.  *    the tilde and following characters have been replaced by
  667.  *    the home directory location for the named user.
  668.  *
  669.  * Results:
  670.  *    The result is a pointer to a static string containing
  671.  *    the _new name.  This name will only persist until the next
  672.  *    call to Tcl_TildeSubst;  save it if you care about it for
  673.  *    the long term.  If there was an error in processing the
  674.  *    tilde, then an error message is left in interp->result
  675.  *    and the return value is NULL.
  676.  *
  677.  *----------------------------------------------------------------------
  678.  */
  679.  
  680. char *
  681. Tcl_TildeSubst(interp, name)
  682.     Tcl_Interp *interp;        /* Interpreter in which to store error
  683.                  * message (if necessary). */
  684.     char *name;            /* File name, which may begin with "~/"
  685.                  * (to indicate current user's home directory)
  686.                  * or "~<user>/" (to indicate any user's
  687.                  * home directory). */
  688. {
  689. #define STATIC_BUF_SIZE 50
  690.     static char staticBuf[STATIC_BUF_SIZE];
  691.     static int curSize = STATIC_BUF_SIZE;
  692.     static char *curBuf = staticBuf;
  693.     char *dir;
  694.     int length;
  695.     int fromPw = 0;
  696.     register char *p;
  697.  
  698.     if (name[0] != '~') {
  699.     return name;
  700.     }
  701.  
  702.     /*
  703.      * First, find the directory name corresponding to the tilde entry.
  704.      */
  705.  
  706.     if ((name[1] == ':') || (name[1] == '\0')) {
  707.         dir = getenv("HOME");
  708.         if (dir == NULL) {
  709.             Tcl_ResetResult(interp);
  710.             Tcl_AppendResult(interp, "couldn't find HOME env. variable to expand \"",
  711.                                 name, "\" ", (char *) NULL);
  712.             return NULL;
  713.             }
  714.         p = name+1;
  715.         }
  716.     else {
  717.         for (p = &name[1]; (*p != 0) && (*p != ':'); p++) {
  718.             /* Null body;  just find end of name. */
  719.             }
  720.         length = p-&name[1];
  721.         if (length >= curSize) {
  722.             length = curSize-1;
  723.             }
  724.         memcpy(curBuf, name+1, length);
  725.         curBuf[length] = '\0';
  726.         dir = ":";
  727.         fromPw = 0;
  728.         }
  729.  
  730.     /*
  731.      * Grow the buffer if necessary to make enough space for the
  732.      * full file name.
  733.      */
  734.  
  735.     length = strlen(dir) + strlen(p);
  736.     if (length >= curSize) {
  737.         if (curBuf != staticBuf) {
  738.             ckfree((char *)curBuf);
  739.             }
  740.         curSize = length + 1;
  741.         curBuf = ckalloc((unsigned) curSize);
  742.         }
  743.  
  744.     /*
  745.      * Finally, concatenate the directory name with the remainder
  746.      * of the path in the buffer.
  747.      */
  748.  
  749.     strcpy(curBuf, dir);
  750.     strcat(curBuf, p);
  751.     return curBuf;
  752. }
  753.  
  754. /*
  755.  *----------------------------------------------------------------------
  756.  *
  757.  * Tcl_GlobCmd --
  758.  *
  759.  *    This procedure is invoked to process the "glob" and globok Tcl
  760.  *      commands.  See the user documentation for details on what they
  761.  *      do.
  762.  *
  763.  * Results:
  764.  *    A standard Tcl result.
  765.  *
  766.  * Side effects:
  767.  *    See the user documentation.
  768.  *
  769.  *----------------------------------------------------------------------
  770.  */
  771.  
  772.     /* ARGSUSED */
  773. int
  774. Tcl_GlobCmd(dummy, interp, argc, argv)
  775.     ClientData dummy;            /* Not used. */
  776.     Tcl_Interp *interp;            /* Current interpreter. */
  777.     int argc;                /* Number of arguments. */
  778.     char **argv;            /* Argument strings. */
  779. {
  780.    /*  GlobResult globRes; */
  781.     char staticSpace[TCL_RESULT_SIZE];
  782.     int i, result, noComplain = 0;
  783.     int sargc;                /* Number of arguments. */
  784.     char **sargv;            /* Argument strings. */
  785. #pragma unused (dummy)
  786.  
  787.     sargc = argc; sargv = argv;
  788.     
  789.     _glob_debug_level = 0;
  790.     _glob_show_type = 0;
  791.     _glob_show_creator = 0;
  792.     _glob_show_invisibles = 0;
  793.     while (argv[1][0] == '-') {
  794.         if (strcmp(argv[1], "-nocomplain") == 0) {
  795.             noComplain = 1;
  796.             argc--; argv++;
  797.             }
  798.         else if (argv[1][1] == 'd') {
  799.             _glob_debug_level = 1;
  800.             if (argv[1][2] >= '0' && argv[1][2] <= '9')
  801.                 _glob_debug_level = argv[1][2] - '0';
  802.             argc--; argv++;
  803.             }
  804.         else if (argv[1][1] == 'i' && argv[1][2] == '\0') {
  805.             _glob_show_invisibles = 1;
  806.             argc--; argv++;
  807.             }
  808.         else if (argv[1][1] == 't' && argv[1][2] == '\0') {
  809.             _glob_show_type = 1;
  810.             sprintf((char *)&_glob_type, "%4.4s", argv[2]);
  811.             argc -= 2; argv += 2;
  812.             }
  813.         else if (argv[1][1] == 'c' && argv[1][2] == '\0') {
  814.             _glob_show_creator = 1;
  815.             sprintf((char *)&_glob_creator, "%4.4s", argv[2]);
  816.             argc -= 2; argv += 2;
  817.             }
  818.         else
  819.             break;
  820.         }
  821.     
  822.     if (_glob_debug_level) {
  823.         for (i = 0 ; i < sargc ; i++)
  824.             Feedback("GLOB: ARGV[%d] = '%s'", i, sargv[i]);
  825.         }
  826.     
  827.     for (i = 1; i < argc; i++) {
  828.         char *thisName;
  829.     
  830.         /*
  831.          * Do special checks for names starting at the root and for
  832.          * names beginning with ~.  Then let DoGlob do the rest.
  833.          */
  834.     
  835.         thisName = argv[i];
  836.         if (*thisName == '~') {
  837.             thisName = Tcl_TildeSubst(interp, thisName);
  838.             if (thisName == NULL) {
  839.                 return TCL_ERROR;
  840.                 }
  841.             }
  842.         
  843.         if (*thisName == ':') {
  844.             result = DoGlob(interp, ":", thisName+1);
  845.             }
  846.         else {
  847.             result = DoGlob(interp, "", thisName);
  848.             }
  849.         
  850.         if (result != TCL_OK) {
  851.             return result;
  852.             }
  853.         }
  854.     
  855.     if ((*interp->result == 0) && !noComplain) {
  856.         interp->result = "no files matched glob pattern(s)";
  857.         return TCL_ERROR;
  858.         }
  859.     
  860.     return TCL_OK;
  861.     }
  862.